#include "file_manager.h"
#include "page.h"
#include "buffer.h"

FileManager* FileManager::instance = nullptr;


//单例
FileManager* FileManager::getInstance()
{
    if (instance == nullptr)
	instance = new FileManager;
    return instance;
}
void FileManager::release()
{
    delete instance;
    instance = nullptr;
}

FileManager::~FileManager()
{
}

void FileManager::update(const char* id, Stat* newContent)
{
    string s(id);
    string doi = s.substr(0, 6);
    string fid = s.substr(6, 3);
    string bid = s.substr(9, 6);
    string rid = s.substr(15, 3);
    //找到页面
    Page* page = getPage(fid, bid);
    page->update(id, newContent);
}

void FileManager::multipleUpdate(vector<string> ids, vector<Stat*> records)
{
    if (ids.size() != records.size()) {
	return;
    }
    else {
	for (int i = 0; i < ids.size(); i++) {
	    update(ids[i].c_str(), records[i]);
	}
    }
}

void FileManager::multipleRemove(vector<string> ids)
{
    //历史
    string hisdoi, hisfid, hisbid, hisrid;
    //当前
    file* curfile = nullptr;
    Page* curPage = nullptr;

    for (auto id : ids) {
	string s(id);
	string doi = s.substr(0, 6);
	string fid = s.substr(6, 3);
	string bid = s.substr(9, 6);
	string rid = s.substr(15, 3);
	if (doi != hisdoi)
	    hisdoi = doi;
	if (fid != hisfid) {
	    hisfid = fid;
	    curfile = getFile(fid);
	}
	if (bid != hisbid) {
	    hisbid = bid;
	    releasePage(curPage);
	    curPage = getPage(curfile->fileid64, bid);
	}
	curPage->remove(id.c_str());
    }
}

vector<string> FileManager::multiplePut(vector<Stat*> records)
{
    vector<string> res;

    file* curfile = getLastAvailableFile();
    if (curfile == nullptr) {
	allocFile();
	curfile = getLastAvailableFile();
    }

    Page* curPage = getLastAvailablePage(curfile->fileid64);
    if (curPage == nullptr) {
	allocPage(curfile->fileid64);
	curPage = getLastAvailablePage(curfile->fileid64);
	if (curPage == nullptr) {
		throw "return nullptr";
	}
    }

    for (auto r : records) {
	if (isFileFull(curfile->fileid64) && curPage->isFull()) {
	    allocFile();
	    curfile = getLastAvailableFile();
	    releasePage(curPage);
	    curPage = getLastAvailablePage(curfile->fileid64);
	    if (curPage == nullptr) {
		throw "return nullptr";
	    }
	}

	if (curPage->isFull()) {
	    allocPage(curfile->fileid64);
	    releasePage(curPage);
	    curPage = getLastAvailablePage(curfile->fileid64);
	}

	if (curPage->isAbleToInput(r)) {
	    res.push_back(curPage->put(r));
	}
	else {
	    if (isFileFull(curfile->fileid64)) {
		allocFile();
		curfile = getLastAvailableFile();
		releasePage(curPage);
		curPage = getLastAvailablePage(curfile->fileid64);
		if (curPage == nullptr) {
		    throw "return nullptr";
		}
	    }
	    else {
		allocPage(curfile->fileid64);
		releasePage(curPage);
		curPage = getLastAvailablePage(curfile->fileid64);
		if (curPage == nullptr) {
		    throw "return nullptr";
		}
	    }

	    //插入
	    res.push_back(curPage->put(r));

	}
    }
    releasePage(curPage);
    return res;
}

vector<Stat*> FileManager::multipleGet(vector<string> ids)
{
    vector<Stat*> res;
    string hisdoi, hisfid, hisbid, hisrid;
    file* curfile = nullptr;
    Page* curpage = nullptr;
    for (auto id : ids) {
	string s(id);
	string doi = s.substr(0, 6);
	string fid = s.substr(6, 3);
	string bid = s.substr(9, 6);
	string rid = s.substr(15, 3);
	if (doi != hisdoi)
	    hisdoi = doi;
	if (fid != hisfid) {
	    hisfid = fid;
	    curfile = getFile(fid);
	}
	if (bid != hisbid) {
	    hisbid = bid;
	    releasePage(curpage);
	    curpage = getPage(curfile->fileid64, bid);
	}
	Stat* r = curpage->get(id.c_str());
	if (r != nullptr) {
	    res.push_back(r);
	}
    }
    return res;
}



bool FileManager::isFileFull(string fileid)
{
    return getFile(fileid)->pageNum >= PAGES_PER_FILE;
}

bool FileManager::isAbleToInput(string fileid, string pageid, Stat* content)
{
    Page* page = getPage(fileid, pageid);
    bool res = page->isAbleToInput(content);
    //临时删除
    delete page;
    return res;
}

bool FileManager::isAbleToInput(Page* page, Stat* content)
{
    return page->isAbleToInput(content);
}

string FileManager::allocPage(string fileid, PageType pt)
{
    file* curfile = getFile(fileid);

    string pageid = Conv64::to_64(curfile->pageNum, 6);
    Page* page = new Page(curfile->fileid64, pageid.c_str(), pt);
    page->writePage();
    delete page;
    curfile->pageNum++;
    return pageid;
}


string FileManager::allocFile()
{
    int maxid = -1;
    for (auto item : files) {
	if (item.fileid > maxid)
	    maxid = item.fileid;
    }
    file newfile = file(maxid + 1);
    files.push_back(newfile);
    return files[files.size() - 1].fileid64;
}





Page* FileManager::getPage(string fileid, string pageid)
{
    //��ȥBuffer������
    Buffer* buffer = Buffer::getInstance();
    return buffer->getPageAsync(fileid, pageid);
}

file* FileManager::getFile(string fileid)
{
    for (unsigned int i = 0; i < files.size(); i++) {
	if (strcmp(files[i].fileid64, fileid.c_str()) == 0)
	    return &files[i];
    }
    return nullptr;
}

file* FileManager::getLastAvailableFile()
{
    if (files.size() == 0)
	return nullptr;
    file* curfile = &files[files.size() - 1];
    if (!isFileFull(curfile->fileid64))
	return curfile;
    else
	return nullptr;
}

Page* FileManager::getLastAvailablePage(string fileid) {
    file* curfile = getFile(fileid);
    if (curfile == nullptr) {
	return nullptr;
    }
    if (curfile->pageNum == 0)
	allocPage(fileid);

    Page* curpage = Buffer::getInstance()->getPageAsync(fileid, Conv64::to_64(curfile->pageNum - 1, 6));
    if (curpage->isFull()) {
	string pageid = allocPage(fileid);
	curpage->setNextPageid(pageid);
	curpage->updateBuffer();
	curpage = Buffer::getInstance()->getPageAsync(fileid, pageid);
    }
    return curpage;
}

void FileManager::releasePage(Page* page)
{
    if (page == nullptr)
	return;
    page->writePage();
    delete page;
}
